1 module scaler; 2 3 import std.algorithm; 4 import std.math; 5 import std.stdio; 6 7 // Retro scalers for NES native 256 * 240 RGBA image 8 9 private enum NATIVE_WIDTH = 256; 10 private enum NATIVE_HEIGHT = 240; 11 12 interface Scaler { 13 int factor(); 14 ubyte* scale(ubyte* src); 15 } 16 17 class NoScaler : Scaler { 18 int factor() { 19 return 1; 20 } 21 22 ubyte* scale(ubyte* src) { 23 return src; 24 } 25 } 26 27 class Scale2x : Scaler { 28 this() { 29 this.buffer = new ubyte[NATIVE_WIDTH * 2 * NATIVE_HEIGHT * 2 * 4]; 30 } 31 32 int factor() { 33 return 2; 34 } 35 36 ubyte* scale(ubyte* src) { 37 ubyte* dst = this.buffer.ptr; 38 39 int width = NATIVE_WIDTH; 40 int height = NATIVE_HEIGHT; 41 int srcPitch = width * 4; 42 int dstPitch = width * 2 * 4; 43 44 int looph, loopw; 45 46 uint e0, e1, e2, e3, b, d, e, f, h; 47 48 for (looph = 0; looph < height; ++looph) { 49 for(loopw = 0; loopw < width; ++loopw) { 50 b = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * loopw)); 51 d = *cast(uint*)(src + (looph * srcPitch) + (4 * max(0, loopw - 1))); 52 e = *cast(uint*)(src + (looph * srcPitch) + (4 * loopw)); 53 f = *cast(uint*)(src + (looph * srcPitch) + (4 * min(width - 1, loopw + 1))); 54 h = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * loopw)); 55 56 e0 = d == b && b != f && d != h ? d : e; 57 e1 = b == f && b != d && f != h ? f : e; 58 e2 = d == h && d != b && h != f ? d : e; 59 e3 = h == f && d != h && b != f ? f : e; 60 61 *cast(uint*)(dst + looph * 2 * dstPitch + loopw * 2 * 4) = e0; 62 *cast(uint*)(dst + looph * 2 * dstPitch + (loopw * 2 + 1) * 4) = e1; 63 *cast(uint*)(dst + (looph * 2 +1)* dstPitch + loopw * 2 * 4) = e2; 64 *cast(uint*)(dst + (looph * 2 +1) * dstPitch + (loopw * 2 + 1) * 4) = e3; 65 } 66 } 67 68 return dst; 69 } 70 71 private: 72 ubyte[] buffer; 73 } 74 75 class Scale3x : Scaler { 76 this() { 77 this.buffer = new ubyte[NATIVE_WIDTH * 3 * NATIVE_HEIGHT * 3 * 4]; 78 } 79 80 int factor() { 81 return 3; 82 } 83 84 ubyte* scale(ubyte* src) { 85 ubyte* dst = this.buffer.ptr; 86 87 int width = NATIVE_WIDTH; 88 int height = NATIVE_HEIGHT; 89 int srcPitch = width * 4; 90 int dstPitch = width * 3 * 4; 91 92 int looph, loopw; 93 94 uint e0, e1, e2, e3, e4, e5, e6, e7, e8; 95 uint a, b, c, d, e, f, g, h, i; 96 97 for (looph = 0; looph < height; ++looph) { 98 for(loopw = 0; loopw < width; ++loopw) { 99 a = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * max(0, loopw - 1))); 100 b = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * loopw)); 101 c = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * min(width - 1, loopw + 1))); 102 d = *cast(uint*)(src + (looph * srcPitch) + (4 * max(0, loopw - 1))); 103 e = *cast(uint*)(src + (looph * srcPitch) + (4 * loopw)); 104 f = *cast(uint*)(src + (looph * srcPitch) + (4 * min(width - 1, loopw + 1))); 105 g = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * max(0, loopw - 1))); 106 h = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * loopw)); 107 i = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * min(width - 1, loopw + 1))); 108 109 if (b != h && d != f) { 110 e0 = d == b ? d : e; 111 e1 = (d == b && e != c) || (b == f && e != a) ? b : e; 112 e2 = b == f ? f : e; 113 e3 = (d == b && e != g) || (d == h && e != a) ? d : e; 114 e4 = e; 115 e5 = (b == f && e != i) || (h == f && e != c) ? f : e; 116 e6 = d == h ? d : e; 117 e7 = (d == h && e != i) || (h == f && e != g) ? h : e; 118 e8 = h == f ? f : e; 119 } else { 120 e0 = e; 121 e1 = e; 122 e2 = e; 123 e3 = e; 124 e4 = e; 125 e5 = e; 126 e6 = e; 127 e7 = e; 128 e8 = e; 129 } 130 131 *cast(uint*)(dst + looph * 3 * dstPitch + loopw * 3 * 4) = e0; 132 *cast(uint*)(dst + looph * 3 * dstPitch + (loopw * 3 + 1) * 4) = e1; 133 *cast(uint*)(dst + looph * 3 * dstPitch + (loopw * 3 + 2) * 4) = e2; 134 *cast(uint*)(dst + (looph * 3 + 1) * dstPitch + loopw * 3 * 4) = e3; 135 *cast(uint*)(dst + (looph * 3 + 1) * dstPitch + (loopw * 3 + 1) * 4) = e4; 136 *cast(uint*)(dst + (looph * 3 + 1) * dstPitch + (loopw * 3 + 2) * 4) = e5; 137 *cast(uint*)(dst + (looph * 3 + 2) * dstPitch + loopw * 3 * 4) = e6; 138 *cast(uint*)(dst + (looph * 3 + 2) * dstPitch + (loopw * 3 + 1) * 4) = e7; 139 *cast(uint*)(dst + (looph * 3 + 2) * dstPitch + (loopw * 3 + 2) * 4) = e8; 140 } 141 } 142 143 return dst; 144 } 145 146 private: 147 ubyte[] buffer; 148 } 149 150 151 class Scale3xFaster : Scaler { 152 this() { 153 this.buffer = new ubyte[NATIVE_WIDTH * 3 * NATIVE_HEIGHT * 3 * 4]; 154 } 155 156 int factor() { 157 return 3; 158 } 159 160 ubyte* scale(ubyte* srcData) { 161 uint* src = cast(uint*)srcData; 162 uint* dst = cast(uint*)this.buffer.ptr; 163 164 int width = NATIVE_WIDTH; 165 int height = NATIVE_HEIGHT; 166 int srcPitch = width; 167 int dstPitch = width * 3; 168 169 // First row 170 scale3x_32_def_whole(dst, dst + dstPitch, dst + (dstPitch * 2), 171 src, src, src + srcPitch, width); 172 173 // Middle rows 174 foreach(i; 1 .. 239) { 175 dst += dstPitch * 3; 176 177 scale3x_32_def_whole(dst, dst + dstPitch, dst + (dstPitch * 2), 178 src, src + srcPitch, src + (srcPitch * 2), width); 179 180 src += srcPitch; 181 } 182 183 // Last row 184 dst += dstPitch * 3; 185 186 scale3x_32_def_whole(dst, dst + dstPitch, dst + (dstPitch * 2), 187 src, src + srcPitch, src + srcPitch, width); 188 189 return cast(ubyte*)this.buffer.ptr; 190 } 191 192 private: 193 ubyte[] buffer; 194 195 void scale3x_32_def_whole(uint* dst0, uint* dst1, uint* dst2, const(uint)* src0, 196 const(uint)* src1, const(uint)* src2, uint count) 197 { 198 assert(count >= 2); 199 200 /* first pixel */ 201 if (src0[0] != src2[0] && src1[0] != src1[1]) { 202 dst0[0] = src1[0]; 203 dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; 204 dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; 205 dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; 206 dst1[1] = src1[0]; 207 dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; 208 dst2[0] = src1[0]; 209 dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; 210 dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; 211 } else { 212 dst0[0] = src1[0]; 213 dst0[1] = src1[0]; 214 dst0[2] = src1[0]; 215 dst1[0] = src1[0]; 216 dst1[1] = src1[0]; 217 dst1[2] = src1[0]; 218 dst2[0] = src1[0]; 219 dst2[1] = src1[0]; 220 dst2[2] = src1[0]; 221 } 222 ++src0; 223 ++src1; 224 ++src2; 225 dst0 += 3; 226 dst1 += 3; 227 dst2 += 3; 228 229 /* central pixels */ 230 count -= 2; 231 while (count) { 232 if (src0[0] != src2[0] && src1[-1] != src1[1]) { 233 dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; 234 dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; 235 dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; 236 dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; 237 dst1[1] = src1[0]; 238 dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; 239 dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; 240 dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; 241 dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; 242 } else { 243 dst0[0] = src1[0]; 244 dst0[1] = src1[0]; 245 dst0[2] = src1[0]; 246 dst1[0] = src1[0]; 247 dst1[1] = src1[0]; 248 dst1[2] = src1[0]; 249 dst2[0] = src1[0]; 250 dst2[1] = src1[0]; 251 dst2[2] = src1[0]; 252 } 253 254 ++src0; 255 ++src1; 256 ++src2; 257 dst0 += 3; 258 dst1 += 3; 259 dst2 += 3; 260 --count; 261 } 262 263 /* last pixel */ 264 if (src0[0] != src2[0] && src1[-1] != src1[0]) { 265 dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; 266 dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; 267 dst0[2] = src1[0]; 268 dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; 269 dst1[1] = src1[0]; 270 dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; 271 dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; 272 dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; 273 dst2[2] = src1[0]; 274 } else { 275 dst0[0] = src1[0]; 276 dst0[1] = src1[0]; 277 dst0[2] = src1[0]; 278 dst1[0] = src1[0]; 279 dst1[1] = src1[0]; 280 dst1[2] = src1[0]; 281 dst2[0] = src1[0]; 282 dst2[1] = src1[0]; 283 dst2[2] = src1[0]; 284 } 285 } 286 }